package com.songlanyun.modules.opus.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.songlanyun.common.enums.IntegralConstant;
import com.songlanyun.common.enums.OpusConstant;
import com.songlanyun.common.enums.PrivateMessageConstant;
import com.songlanyun.common.enums.SysConstant;
import com.songlanyun.common.exception.RRException;
import com.songlanyun.common.utils.*;
import com.songlanyun.common.validator.ValidatorUtils;
import com.songlanyun.modules.account.service.UserInfoService;
import com.songlanyun.modules.apphome.service.HomePageService;
import com.songlanyun.modules.article.entity.Article;
import com.songlanyun.modules.article.service.ArticleService;
import com.songlanyun.modules.comment.entity.UserComment;
import com.songlanyun.modules.comment.service.UserCommentService;
import com.songlanyun.modules.concerns.service.UserConcernService;
import com.songlanyun.modules.exception.BbsException;
import com.songlanyun.modules.integralconfig.entity.IntegralConfig;
import com.songlanyun.modules.integralconfig.service.IntegralConfigService;
import com.songlanyun.modules.integralrecord.entity.IntegralRecord;
import com.songlanyun.modules.integralrecord.service.IntegralRecordService;
import com.songlanyun.modules.like.service.LikeRecordService;
import com.songlanyun.modules.message.model.InteractionMessage;
import com.songlanyun.modules.message.model.to.MessageTO;
import com.songlanyun.modules.message.model.to.PrivateMessageTO;
import com.songlanyun.modules.message.service.PrivateMessageService;
import com.songlanyun.modules.message.service.SendPrivateMessageMsgService;
import com.songlanyun.modules.opus.dao.OpusDao;
import com.songlanyun.modules.opus.entity.Opus;
import com.songlanyun.modules.opus.entity.OpusTheme;
import com.songlanyun.modules.opus.model.dto.BbsDTO;
import com.songlanyun.modules.opus.model.dto.OpusDTO;
import com.songlanyun.modules.opus.service.OpusGoodsService;
import com.songlanyun.modules.opus.service.OpusService;
import com.songlanyun.modules.opus.service.OpusThemeService;
import com.songlanyun.modules.opus.service.OpusThemeSystemService;
import com.songlanyun.modules.rabbitmq.util.SpringContextUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.Size;
import java.util.*;

import static com.songlanyun.common.utils.RedisKeys.BbsKey;


@Service("opusService")
public class OpusServiceImpl extends ServiceImpl<OpusDao, Opus> implements OpusService {

    @Resource
    private OpusThemeService opusThemeService;
    @Resource
    private OpusGoodsService opusGoodsService;
    @Resource
    private LikeRecordService likeRecordService;
    @Resource
    private UserCommentService userCommentService;
    @Resource
    private IntegralRecordService integralRecordService;
    @Resource
    private IntegralConfigService integralConfigService;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private PrivateMessageService privateMessageService;
    @Resource
    private UserInfoService userInfoService;
    @Resource
    private SendPrivateMessageMsgService sendPrivateMessageMsgService;
    @Resource
    private UserConcernService userConcernService;
    @Resource
    private ArticleService articleService;
    @Resource
    private OpusThemeSystemService opusThemeSystemService;
    @Resource
    private HomePageService homePageService;

    @Override
    public Opus create(Opus entity) {
        ValidatorUtils.validateEntity(entity);
        this.baseMapper.insert(entity);
        return entity;
    }

    @Override
    public Opus modify(Opus entity) {
        ValidatorUtils.validateEntity(entity);
        this.baseMapper.updateById(entity);
        return entity;
    }

    @Override
    public void remove(Long id) {

    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<Opus> page = this.baseMapper.opusPage(new Query<Opus>().getPage(params), params);

        //加载话题(字符串逗号拼接)
        page.getRecords().forEach(Opus::loadOpusThemeList);
        return new PageUtils(page);
    }


    @Override
    public PageUtils allList(Map<String, Object> params) {
        IPage<Opus> page = this.baseMapper.opusAppPage(new Query<Opus>().getPage(params), params);
        page.getRecords().forEach(o -> {
            o.setLikeNum(likeRecordService.likeSum(OpusConstant.ModelType.OPUS, o.getId()));
        });

        return new PageUtils(page);
    }

    @Override
    public PageUtils themeList(Map<String, Object> params) {
        IPage<Opus> page = this.baseMapper.opusAppThemePage(new Query<Opus>().getPage(params), params);
        page.getRecords().forEach(o -> {
            o.setLikeNum(likeRecordService.likeSum(OpusConstant.ModelType.OPUS, o.getId()));
        });

        return new PageUtils(page);
    }

    @Override
    public PageUtils meList(Map<String, Object> params) {
        IPage<Opus> page = this.baseMapper.mePage(new Query<Opus>().getPage(params), params);
        page.getRecords().forEach(o -> {
            o.setLikeNum(likeRecordService.likeSum(OpusConstant.ModelType.OPUS, o.getId()));
        });

        return new PageUtils(page);
    }

    @Override
    @Transactional
    public Opus add(Long userId, OpusDTO dto) {
        //保存作品
        Opus opus = new Opus();
        BeanUtil.copyPropertiesIgnoreNull(dto, opus);
        opus.setPublisherId(userId);//发布会员
        opus.setOpusType(OpusConstant.OpusType.getByCode(dto.getOpusType()));//作品类型
        opus.setPublisherType(OpusConstant.PublisherType.Member);//发布人身份
        opus.setOpusStatus(OpusConstant.VerifyStatus.WAIT);//待审核
        this.baseMapper.insert(opus);

        //保存作品与话题的关系
        if (dto.getThemeIds() != null && dto.getThemeIds().length > 0) {
            List<Long> themeIdList = Arrays.asList(dto.getThemeIds());
            opusThemeService.saveOpusTheme(opus.getId(), themeIdList);
//            //更新话题的作品数
//            opusThemeSystemService.updateOpusNum(themeIdList, 1);
        }
        //保存作品与商品的关系
        if (dto.getGoodsIds() != null && dto.getGoodsIds().length > 0) {
            opusGoodsService.saveOpusGoods(opus.getId(), Arrays.asList(dto.getGoodsIds()));
        }

        return opus;
    }

    @Override
    @Transactional
    public void deleteById(Long oid) {

        //删除作品
        this.baseMapper.deleteById(oid);

        //删除作品与话题的关系
        opusThemeService.removeByOid(oid);
        //删除作品与商品的关系
        opusGoodsService.removeByOid(oid);

//        List<OpusTheme> opusThemeList = opusThemeService.listByOpusId(oid);
//        List<Long> themeIdList = new ArrayList<>();
//        if (opusThemeList != null) {
//            opusThemeList.forEach(ot -> {
//                themeIdList.add(ot.getOpusId());
//            });
//            if (themeIdList.size() > 0) {
//                //更新话题的作品数
//                opusThemeSystemService.updateOpusNum(themeIdList, -1);
//            }
//        }

        //删除浏览、点赞、分享、评论等记录
        homePageService.delRecord(OpusConstant.ModelType.OPUS, oid);

    }

    @Override
    public Opus getById(Long id, boolean isThrow) {
        Opus opus = this.baseMapper.selectById(id);
        if (opus == null && isThrow) {
            throw new RRException(BbsException.OPUS_NOT_EXISTS);
        }
        return opus;
    }

    @Override
    public Opus appInfo(Long userId, Long id) {
        Opus opus = this.baseMapper.appInfo(id);
        if (opus != null) {
            opus.loadOpusGoodsList();//所有商品
            opus.loadUserCommentList();//所有评论
            //是否是自己发布的作品
            opus.loadIsMe(userId);

            //查询用户是否关注该作品发布者
            if (userId != 0) {
                //是否关注
                opus.setIsConcern(userConcernService.isConcern(userId, opus.getPublisherId()));
                opus.loadIsLike(userId);
            }
        }
        return opus;
    }


    @Override
    public Opus adminInfo(Long id) {
        Opus opus = this.baseMapper.selectById(id);
        if (opus != null) {
            opus.loadOpusGoodsList();//所有商品
//            opus.loadOpusThemeList();//所有主题
        }
        return opus;
    }


    //    @Override
//    @Transactional
//    public void browse(Long id, Long userId) {
//        Opus opus = this.getById(id, true);
//        //增加作品浏览总数
//        opus.addBrowseSum(1);
//        if (userId != 0) {//登录后记录浏览记录
//            //保存作品浏览记录
//            Boolean browse = browseRecordService.browse(OpusConstant.ModelType.OPUS, id, userId, opus.getPublisherId());
//            if (browse) {//浏览成功
//                //增加作品浏览人数
//                opus.addBrowseNum(1);
//            }
//            //是否已赠送积分
//            Boolean exists = integralRecordService.exists(OpusConstant.ModelType.OPUS, id, userId, OpusConstant.InnerMode.BROWSE);
//            if (exists) {
//                //浏览人获得积分  -- 发送mq消息
//                IntegralConfig config = integralConfigService.get();
//                sendMsg(userId, config.getBrowse(), OpusConstant.InnerMode.BROWSE.getCode(), OpusConstant.IntegralModelType.OPUS, id, "浏览作品获得积分");
//                //发布人获得积分 -- 发送mq消息
//                sendMsg(opus.getPublisherId(), config.getByBrowse(), OpusConstant.InnerMode.BY_BROWSE.getCode(), OpusConstant.IntegralModelType.OPUS, id, "发布的作品被浏览获得积分");
//            }
//        }
//
//    }
//
//    @Override
//    @Transactional
//    public void like(Long id, Long userId) {
//        Opus opus = this.getById(id, true);
//        //保存作品点赞记录
//        Boolean like = likeRecordService.like(OpusConstant.ModelType.OPUS, id, userId, opus.getPublisherId());
//        if (like) {//点赞成功
//            //增加作品点赞人数
//            opus.addLikeNum(1);
//        }
//        //是否已赠送积分
//        Boolean exists = integralRecordService.exists(OpusConstant.ModelType.OPUS, id, userId, OpusConstant.InnerMode.LIKE);
//        if (exists) {
//            //点赞人获得积分  -- 发送mq消息
//            sendMsg(userId, OpusConstant.InnerMode.LIKE.getCode(), OpusConstant.IntegralModelType.OPUS, id, "点赞作品获得积分");
//            //发布人获得积分 -- 发送mq消息
//            sendMsg(opus.getPublisherId(), OpusConstant.InnerMode.BY_LIKE.getCode(), OpusConstant.IntegralModelType.OPUS, id, "发布的作品被点赞获得积分");
//        }
//    }
//
//    @Override
//    @Transactional
//    public void share(Long id, Long userId) {
//        Opus opus = this.getById(id, true);
//        //增加作品分享总数
//        opus.addShareSum(1);
//        //保存作品分享记录
//        reprintRecordService.share(OpusConstant.ModelType.OPUS, id, userId, opus.getPublisherId());
//
//        //是否已赠送积分
//        Boolean exists = integralRecordService.exists(OpusConstant.ModelType.OPUS, id, userId, OpusConstant.InnerMode.SHARE);
//        if (exists) {
//            //增加作品分享人数
//            opus.addShareNum(1);
//
//            //分享人获得积分  -- 发送mq消息
//            sendMsg(userId, OpusConstant.InnerMode.SHARE.getCode(), OpusConstant.IntegralModelType.OPUS, id, "分享作品获得积分");
//            //发布人获得积分 -- 发送mq消息
//            sendMsg(opus.getPublisherId(), OpusConstant.InnerMode.BY_SHARE.getCode(), OpusConstant.IntegralModelType.OPUS, id, "发布的作品被分享获得积分");
//        }
//
//    }
//
    @Override
    @Transactional
    public void comment(UserComment userComment) {

        //增加评论总数
        Opus opus = this.getById(userComment.getModelId(), true);

        //根据opusId、userId、去查询评论表记录，看是否增加评论人数记录
        Boolean comment = userCommentService.comment(userComment);
        //是否有效  有效评论才进行下面操作
        if (userComment.getIsShow()) {
            //增加评论总数
            opus.addCommentSum(1);
            if (comment) {
                //增加作品评论人数
                opus.addCommentNum(1);
            }
            //是否已赠送积分
            Boolean exists = integralRecordService.exists(OpusConstant.ModelType.OPUS, opus.getId(),
                    userComment.getCommentUserId(), IntegralConstant.InnerMode.COMMENT);
            if (!exists) {
                IntegralConfig config = integralConfigService.get();
                if (config != null) {
                    //评论人获得积分
                    IntegralRecord record = new IntegralRecord(userComment.getCommentUserId(), config.getComment(), IntegralConstant.InnerMode.COMMENT,
                            IntegralConstant.IntegralModelType.OPUS, userComment.getId(), "评论作品获得积分");
                    integralRecordService.inner(record);
                    //发布人获得积分
                    IntegralRecord recordBy = new IntegralRecord(opus.getPublisherId(), config.getByComment(), IntegralConstant.InnerMode.BY_COMMENT,
                            IntegralConstant.IntegralModelType.OPUS, userComment.getId(), "发布的作品被评论获得积分");
                    integralRecordService.inner(recordBy);
                }
            }

            //保存站内消息 如果是自己评论了自己的作品，则不保存消息
            if (!userComment.getCommentUserId().equals(opus.getPublisherId())) {
                sendPrivateMessageMsgService.send(new MessageTO(PrivateMessageConstant.MessageType.INTERACTION_MESSAGE,
                        PrivateMessageConstant.ModelType.OPUS_LIKE_SHARE_COMMENT, userComment.getModelId(), OpusConstant.BbsType.COMMENT,
                        userComment.getCommentUserId()));
            }
        }

    }

    @Override
    public Opus audit(Opus opus) {
        Opus op = this.getById(opus.getId(), true);
        if (!OpusConstant.VerifyStatus.WAIT.equals(op.getOpusStatus())) {
            throw new RRException(BbsException.OPUS_ALREADY_AUDIT);
        }

        op.setOpusStatus(opus.getOpusStatus());
        op.setAuditRemark(opus.getAuditRemark());
        op.setAuditTime(new Date());
        this.baseMapper.updateById(op);

        return op;
    }

    @Override
    public void stick(Opus opus) {
        Opus op = this.getById(opus.getId(), true);
        op.setOpenStick(opus.getOpenStick());
        this.baseMapper.updateById(op);
    }

    @Override
    @Transactional
    public Map<String, Object> cancelLike(Long id, Long userId) {
        Map<String, Object> map = new HashMap<>();

        Opus opus = this.getById(id, true);
        //删除redis缓存
        Boolean delLikeRedis = likeRecordService.delLikeRedis(userId, BbsKey.LIKE_OPUS_KEY, opus.getId());

        //删除会员对作品的点赞记录
        if (!delLikeRedis && likeRecordService.isLike(userId, OpusConstant.ModelType.OPUS, id)) {
            likeRecordService.delLike(userId, OpusConstant.ModelType.OPUS, id);
            //减少作品点赞人数
            opus.subLikeNum(1);
        }

        map.put("is_like", false);
        map.put("like_num", opus.getLikeNum());
        return map;

    }

    @Override
    @SuppressWarnings("all")
    public Map<String, Object> cacheBbs(BbsDTO dto) {
        OpusConstant.BbsType bbsType = OpusConstant.BbsType.getByCode(dto.getBbsType());
        OpusConstant.ModelType modelType = OpusConstant.ModelType.getByCode(dto.getModelType());

        Map<String, Object> map = null;

        //获取redis中对应key
        String redisKey = getRedisKey(bbsType, modelType);
        if (StringUtils.isBlank(redisKey)) {
            return map;
        }
        BoundHashOperations boundHashOperations = redisUtils.boundHashOps(redisKey);
        //判断是否存在该类型id
        String modelIdStr = String.valueOf(dto.getModelId());
        String userIdStr = String.valueOf(dto.getUserId());

        Map<String, Object> userMap = (Map<String, Object>) boundHashOperations.get(modelIdStr);
        Map<String, Object> numTimeMap = new HashMap<>();//存入数量和最后一次更新时间
        Integer num = dto.getNum();//更新数量
        if (userMap != null) {
            //存在，则查询是否存在该用户的数据
            if (userMap.containsKey(userIdStr)) {
                //存在，则缓存
                numTimeMap = (Map<String, Object>) userMap.get(userIdStr);
                Integer beNum = (Integer) numTimeMap.get("num");
                num = beNum + dto.getNum();//数量+redis中的数量=总数量

            }
        } else {
            //不存在，则创建
            userMap = new HashMap<>();
            num = dto.getNum();
        }

        numTimeMap.put("num", dto.getNum());//更新数量
        numTimeMap.put("time", new Date());//最后一次更新时间
        userMap.put(userIdStr, numTimeMap);
        boundHashOperations.put(modelIdStr, userMap);

        //如果是点赞，则需要返回点赞数据给前端
        if (OpusConstant.BbsType.LIKE == bbsType) {
            map = new HashMap<>();
            map.put("is_like", true);
            //查询点赞总数 （redis+数据库）
            map.put("like_num", likeSum(dto.getUserId(), modelType, dto.getModelId(), dto.getNum()));
        }
        return map;
    }

    /**
     * 数据库点赞数
     *
     * @param modelType
     * @param modelId
     * @return
     */
    private Integer likeSum(Long userId, OpusConstant.ModelType modelType, Long modelId, Integer num) {
        Integer likeNum = 0;
        Integer likeSum = 0;
        switch (modelType) {
            case ARTICLE:
                Article acticle = articleService.getById(modelId);
                if (acticle != null) {
                    likeNum = acticle.getLikeNum();
                }
                if (likeRecordService.isLike(userId, modelType, modelId)) {
                    //已点赞过
                    likeSum = likeNum;
                } else {
                    likeSum = likeNum + num;
                }
                break;
            case OPUS:
                Opus opus = this.baseMapper.selectById(modelId);
                if (opus != null) {
                    likeNum = opus.getLikeNum();
                }
                if (likeRecordService.isLike(userId, modelType, modelId)) {
                    //已点赞过
                    likeSum = likeNum;
                } else {
                    likeSum = likeNum + num;
                }
                break;
        }
        return likeSum;
    }


    //TODO 评论不能从redis中取出保存
//    @Override
//    @Transactional
//    public void commentSave(BoundHashOperations<String, String, Object> result) {
//        //作品评论
//        result.entries().forEach((m, n) -> {
//            AtomicReference<Integer> sum = new AtomicReference<>(0);//评论总数
//            AtomicReference<Integer> num = new AtomicReference<>(0);//评论人数
//            HashMap<String, Object> userMap = (HashMap<String, Object>) n;//{11=111, 22=222}
//
//            //获得作品
//            Long opusId = Long.parseLong(m);
//            Opus opus = this.baseMapper.selectById(opusId);
//
//            if (opus != null && userMap != null) {
//                userMap.forEach((key, value) -> {
//                    Long userId = Long.parseLong(key);//11
//                    Integer count = (Integer) value;//111
//                    sum.set(sum.get() + count);
//
//                    //根据opusId、userId、去查询分享表记录，如果不存在记录，则保存分享记录
//                    Boolean comment = userCommentService.ifComment(OpusConstant.ModelType.OPUS, opusId, userId);
//                    if (comment) {
//                        //计算评论人数，不存在记录时统计
//                        num.set(num.get() + count);
//                    }
//                    //是否已赠送积分
//                    Boolean exists = integralRecordService.exists(OpusConstant.ModelType.OPUS, opusId, userId, OpusConstant.InnerMode.COMMENT);
//                    if (!exists) {
//                        IntegralConfig config = integralConfigService.get();
//                        if (config != null) {
//                            //分享人获得积分
//                            integralRecordService.saveIntegral(userId, config.getBrowse(), BillConstant.BillMode.INCOME, OpusConstant.InnerMode.COMMENT.getCode(),
//                                    OpusConstant.IntegralModelType.OPUS.getCode(), opusId, "评论作品获得积分");
//                            //发布人获得积分
//                            integralRecordService.saveIntegral(opus.getPublisherId(), config.getByBrowse(), BillConstant.BillMode.INCOME, OpusConstant.InnerMode.BY_COMMENT.getCode(),
//                                    OpusConstant.IntegralModelType.OPUS.getCode(), opusId, "发布的作品被评论获得积分");
//                        }
//
//                    }
//                });
//                //更新作品表分享总数，分享人数
//                opus.addCommentNum(num.get());
//                opus.addCommentSum(sum.get());
//            }
//            //删除redis缓存
//            result.delete(m);
//        });
//    }

    private String getRedisKey(OpusConstant.BbsType bbsType, OpusConstant.ModelType modelType) {
        RedisKeys.BbsKey redisKey = null;
        switch (bbsType) {
            case BROWSE:
                switch (modelType) {
                    case OPUS:
                        redisKey = BbsKey.BROWSE_OPUS_KEY;
                        break;
                    case ARTICLE:
                        redisKey = BbsKey.BROWSE_ARTICLE_KEY;
                        break;
                }
                break;
            case LIKE:
                switch (modelType) {
                    case OPUS:
                        redisKey = BbsKey.LIKE_OPUS_KEY;
                        break;
                    case ARTICLE:
                        redisKey = BbsKey.LIKE_ARTICLE_KEY;
                        break;
                }
                break;
            case SHARE:
                switch (modelType) {
                    case OPUS:
                        redisKey = BbsKey.SHARE_OPUS_KEY;
                        break;
                    case ARTICLE:
                        redisKey = BbsKey.SHARE_ARTICLE_KEY;
                        break;
                    case COMMON_GOODS:
                        redisKey = BbsKey.SHARE_COMMON_GOODS_KEY;
                        break;
                    case SERVICE_GOODS:
                        redisKey = BbsKey.SHARE_SERVICE_GOODS_KEY;
                        break;
                    case SECKILL_GOODS:
                        redisKey = BbsKey.SHARE_SECKILL_GOODS_KEY;
                        break;
                    case GROUP_BUY_GOODS:
                        redisKey = BbsKey.SHARE_GROUP_BUY_GOODS_KEY;
                        break;
                }
                break;
            case COMMENT:
                switch (modelType) {
                    case OPUS:
                        redisKey = BbsKey.COMMENT_OPUS_KEY;
                        break;
                    case ARTICLE:
                        redisKey = BbsKey.COMMENT_ARTICLE_KEY;
                        break;
                }
                break;
        }

        if (redisKey == null) {
            log.error("bbs存入缓存时，未获取到redis中对应的key");
            return "";
        }
        return redisKey.getKey();

    }


    /**
     * 发送消息
     */
    @Override
    public void sendMessage(Long userId, Long opusId, OpusConstant.BbsType bbsType, String title) {

        String name = userInfoService.getNameById(userId);
        String remark = String.format("%s了你的%s【%s】", name + bbsType.getMsg(), OpusConstant.ModelType.OPUS.getMsg(), title);
        InteractionMessage interactionMessage = new InteractionMessage(SysConstant.InteractionType.OPUS_INTERACTION, opusId, remark);
        PrivateMessageTO to = new PrivateMessageTO(userId, PrivateMessageConstant.MessageType.INTERACTION_MESSAGE, interactionMessage, remark);

        privateMessageService.saveMessage(to);
//        //发送消息
//        sendMsgService.send(MqConstant.PrivateMessageSendMessageConstant.PRIVATE_MESSAGE_SEND_MESSAGE_EXCHANGE,
//                MqConstant.PrivateMessageSendMessageConstant.PRIVATE_MESSAGE_SEND_MESSAGE_ROUTER
//                , JSONObject.toJSONString(to));
    }

    @Override
    public Integer releaseNum(Long userId) {
        return this.baseMapper.selectCount(
                new LambdaQueryWrapper<Opus>()
//                        .eq(Opus::getOpusStatus, OpusConstant.VerifyStatus.YES)
                        .eq(Opus::getPublisherId, userId)
        );
    }


}
